package lightswitch.server.code.sqlhelper.statement.select;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;



import lightswitch.server.code.sqlhelper.expression.Expression;
import lightswitch.server.code.sqlhelper.expression.NormalTable;
import lightswitch.server.code.sqlhelper.expression.AbstractTable;
import lightswitch.server.code.sqlhelper.statement.Statement;
import lightswitch.server.code.util.StringHelper;

public class Select implements Statement {
	public SelectItems _selectItems;
	public Where _where;
	public From _from;
	public OrderBy _orderBy;
	public GroupBy _groupBy;
	public Having _having;
	private List<From> _parentFromList;
	private boolean isDistinct = false;//DISTINCT
	//private From _parentFrom;
	
//	public Join _join;
	
	public Select(String statement, List<From> list) {
	
		_parentFromList = new ArrayList<>();
		this.parse(statement);
		if(list!=null)
		{
			for(int i=0;i<list.size();i++)
			{
				_parentFromList.add(list.get(i));
			}
		}
	}

	@Override
	public void parse(String statement) {
		// TODO Auto-generated method stub
		String selectItemsString = "";
		String whereString = "";
		String fromString = "";
		String orderByString="";
		String groupByString="";
		String havingString="";
		String remainString ="";
//		String joinString ="";

       statement = StringHelper.NormalizeString(statement);

		Pattern p = Pattern.compile("^((select.[A-Za-z0-9 ,_.\\*]+)from)(.*)$");
		Matcher m = p.matcher(statement);
	
		if (m.find()) {
			selectItemsString = m.group(1).substring(0, m.group(1).length()- "from".length());
			
			
			remainString = "from" +  m.group(3);
		
		}
	
		selectItemsString = StringHelper.NormalizeString(selectItemsString);
		String[] tempList = selectItemsString.split(" ");
		if(tempList.length>2)
		{
			if(	tempList[1].equals("distinct"))
			{
					isDistinct=true;
					selectItemsString=tempList[0];
					for(int k=2;k<tempList.length;k++)
					{
						
						selectItemsString+=(" "+ tempList[k]);
					}
			}
			else
				isDistinct=false;
			
			
			
		}
		
		
		
		List<String> wordList=new ArrayList<>();
		// lay chuoi from..
		if(!remainString.equals(""))
		{
			
		
		wordList = StringHelper.SplitStringInEqualLevelKeepingToken(remainString);
		remainString="";
		boolean fromfound = false;
		for(int i=0;i<wordList.size();i++)
		{
			if(fromfound==false)
			{
				if(wordList.get(i).equals("where")||wordList.get(i).equals("groupby")||wordList.get(i).equals("orderby"))
				{
					fromfound =true;
					remainString+=(" "+wordList.get(i));
				}
				else
				{
					fromString+=(" "+wordList.get(i));
				}
			}
			else
			{
				remainString+=(" "+wordList.get(i));
			}
		}
		}
		else
		{
			//ko co from:
			//throw exception
			return;
		}
		// where
		if(!remainString.equals(""))
		{
			
		wordList.clear();
		wordList = StringHelper.SplitStringInEqualLevelKeepingToken(remainString);
		remainString="";
		boolean wherefound = false;
		for(int i=0;i<wordList.size();i++)
		{
			if(wherefound==false)
			{
				if(wordList.get(i).equals("groupby")||wordList.get(i).equals("orderby"))
				{
					wherefound =true;
					remainString+=(" "+wordList.get(i));
				}
				else
				{
					whereString+=(" "+wordList.get(i));
				}
			}
			else
			{
				remainString+=(" "+wordList.get(i));
			}
		}
		}
	//groupby	
		if(!remainString.equals(""))
		{
			
		wordList.clear();
		wordList = StringHelper.SplitStringInEqualLevelKeepingToken(remainString);
		remainString="";
		boolean groupbyfound = false;
		for(int i=0;i<wordList.size();i++)
		{
			if(groupbyfound==false)
			{
				if(wordList.get(i).equals("having")||wordList.get(i).equals("orderby"))
				{
					groupbyfound =true;
					remainString+=(" "+wordList.get(i));
				}
				else
				{
					groupByString+=(" "+wordList.get(i));
				}
			}
			else
			{
				remainString+=(" "+wordList.get(i));
			}
		}
		}
		//orderby
		if(!remainString.equals(""))
		{
			
		wordList.clear();
		wordList = StringHelper.SplitStringInEqualLevelKeepingToken(remainString);
		remainString="";
		boolean orderbyfound = false;
		for(int i=0;i<wordList.size();i++)
		{
			if(orderbyfound==false)
			{
				if(wordList.get(i).equals("having"))
				{
					orderbyfound =true;
					remainString+=(" "+wordList.get(i));
				}
				else
				{
					orderByString+=(" "+wordList.get(i));
				}
			}
			else
			{
				remainString+=(" "+wordList.get(i));
			}
		}
		}
		havingString = remainString;
		
		
	
		_from = new From(fromString,null);
		
		_selectItems = new SelectItems(selectItemsString,_from);
		_where = new Where(whereString,_from);
		_orderBy = new OrderBy(orderByString,_from);
		_groupBy = new GroupBy(groupByString,_from);
		_having = new Having(havingString);
	//	_join = new Join(joinString);

	}

	public AbstractTable execute() {
		// TODO Auto-generated method stub

		List<From> allTableList = new ArrayList<>();
		for(int i=0;i<_parentFromList.size();i++)
		{
			allTableList.add(_parentFromList.get(i));
		}
		allTableList.add(_from);
		
		List<NormalTable> parentTableList = new ArrayList<>();
		List<Expression> parentExpressionList = new ArrayList<>();
		
		//FROM
		for(int i=0;i<_from._tableList.size();i++)
		{
			_from._tableList.get(i).InitTable();
			// các bảng đã có dữ liệu.
		}
		
		for(int i=0;i<_parentFromList.size();i++)
		{
			for(int j=0;j<_parentFromList.get(i)._tableList.size();j++)
			{
				_parentFromList.get(i)._tableList.get(j).InitTable();
					parentTableList.add(_parentFromList.get(i)._tableList.get(j).getAsNormalTable());
					parentExpressionList.add(_parentFromList.get(i)._joinExpression.get(j));
			}
		}		
		//
		//gộp các bảng lại thành 1 bảng
		
		// 
		// WHERE:
		NormalTable _temp=null;
		if(_from._tableList.size()>0)
		{
			_temp = (NormalTable) _from._tableList.get(0).getAsNormalTable();
		}
		else
			return null;
			
		for(int i=1;i<_from._tableList.size();i++)
		{
			_temp = _temp.MultiWith(_from._tableList.get(i).getAsNormalTable(),_from._joinExpression.get(i));
			
		}
		for(int i=0;i<parentTableList.size();i++)
		{
			
				_temp = _temp.MultiWith(parentTableList.get(i),parentExpressionList.get(i));
			
		}
				
		for(int i=0;i<_temp.dataRowList.size();i++)
		{
			
			if(((boolean)_where._whereExpression.GetValue((_temp.dataRowList.get(i)),allTableList))==false)
			{
				_temp.dataRowList.remove(i);
				i--;
			}
		}
		//_table = _temp;
		// SELECT ITEM:
	
		for(int i=0;i<_orderBy._orderItemList.size();i++)
		{
			
			int index = _temp.getIndexOfColumn(_orderBy._orderItemList.get(i).column.table.alias+"."+_orderBy._orderItemList.get(i).column.name);
			_temp.SortTable(index, _orderBy._orderItemList.get(i).orderType);
		}
		
		
		for(int i=0;i<_temp.propertyList.size();i++)
		{
			boolean found = false;
			for(int j=0;j<_selectItems._columnList.size();j++)
			{
			
				if(_selectItems._columnList.get(j).name.equals("*") 
					||  _temp.propertyList.get(i).getName().equals(_selectItems._columnList.get(j).table.alias+"."+_selectItems._columnList.get(j).name))
				{
						_temp.propertyList.get(i).alias=_selectItems._columnList.get(j).alias==""?_temp.propertyList.get(i).getName():_selectItems._columnList.get(j).alias;
						found =true;		
						break;
				}
			}
			if(found==false)
			{
				_temp.RemoveColumn(i);
				i--;
			}
		}
		if(isDistinct)
		{
		for(int i=0;i<_temp.dataRowList.size()-1;i++)
		{
			
			for(int j=i+1;j<_temp.dataRowList.size();j++)
			{
				if(_temp.dataRowList.get(j).isLike(_temp.dataRowList.get(i)))
				{
					_temp.dataRowList.remove(j);
					j--;
				}
			}
	
		}
		}
		
		
		
		
		return _temp;
		
	}
	public String toString()
	{
		String t ="";
	t = t+ 	_selectItems.toString() + "\n";
	t = t+ 	_from.toString() + "\n";
	t = t+ 	_where.toString() + "\n";
	t = t+ 	_orderBy.toString() + "\n";
	t = t+ 	_groupBy.toString() + "\n";
	t = t+ 	_having.toString() + "\n";
		return t;
	}
}
